LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

XML_Schema dtd Dom4j XPath

2023/9/12 后端

XML(Extensible Markup Language)

XML可扩展标记语言,编写XML就是编写标签,与HTML非常类似,扩展名.xml。有良好的人机可读性

hr.xml
<employee>
 <name>张三</name>
 <age>31</age>
 <height>178</height>
</employee>
  • XML与HTML非常相似,都是编写标签
  • XML没有预定义标签,HTML存在大量预定义标签
  • XML重在保存与传输数据,HTML用于显示信息

XML用途

① 程序中有各种设置项提取出来存放在XML文件中,利用XML进行程序的设计

web.xml - web应用配置文件
<web-app>
 <servlet>
  <servlet-name>InitTest</servlet-name>
  <servlet-class>moreservlets.InitServlet</servlet-class>
   <init-param>
    <param-name>param1</param-name>
    <param-value>value1</param-value>
   </init-param>
   <init-param>
    <param-name>param2</param-name>
    <param-value>2</param-value>
   </init-param>
 </servlet>
</web-app>

② 用于保存程序产生的数据 [简单的变成可以把数据库导成xml]

hr.xml
<employee>
 <name>张三</name>
 <age>31</age>
 <height>178</height>
 <salary>7800</salary>
</employee>

③ 网络间的数据传输

webservice底层soap协议
<Envelop>
<Body>
<m:reversexmlns:m="urn:strings-com:IString">
<theString>Hello,World</theString>
</m:reversexmlns:m>
</Body>
</Envelop>

XML文档结构

  • 第一行必须是XML声明

    XML声明说明XML文档的基本信息,包括版本号与字符集,写在XML第一行

    <?xml version="1.0" encoding="UTF-8"?>
    version代表版本号1.0
    encoding UTF-8设置字符集,用于支持中文
    <!-- 人力资源管理系统 -->
    <hr>
      <employee no="3309">
        <name>张三</name>
        <age>31</age>
        <salary>4000</salary>
        <department>
          <dname>会计部</dname>
          <addresss>XX大厦-B103</addresss>
        </department>
      </employee>
    
      <employee no="3310">
        <name>李四</name>
        <age>23</age>
        <salary>4000</salary>
        <department>
          <dname>工程部</dname>
          <addresss>XX大厦-B104</addresss>
        </department>
      </employee>
    </hr>
    
  • 有且只有一个根节点

  • XML标签的书写规则与HTML相同

合法的标签名

标签名要有意义
建议使用英文,小写字母,单词之间使用”-“分割

适当的注释与缩进

适当的注释与缩进可以让XML文档更容易阅读

合理使用属性

标签属性用于描述标签不可或缺的信息
对标签分组或者为标签设置Id时常用属性表示

<shop-cart>
<item sn="771938" category="电器">
 <name>XXX空调</name>
 <price>2000.00</price>
 <num>1</num>
</item>
<item sn="890321" category="食品">
 <name>法式面包</name>
 <price>10.00</price>
 <num>5</num>
</item>
</shop-cart>
特殊字符与CDATA标签

标签体中,出现”<”、”>”特殊字符,会破坏文档结构

错误的XML
<question> 1+4<3是否正确?</question>

解决方案①:使用实体引用

实体引用 对应符号 说明
& It; < 小于
& gt; > 大于
& amp; & 和号
& apos; 单引号
& quot; 双引号
修改后的XML
<question> 1+4&lt;3是否正确?</question>

解决方案②:使用CDATA标签
CDATA指的是不应由XML解析器进行解析的文本数据
从”< ![CDATA[“开始,到”]] >“结束

<lesson>
<content>
 <![CDATA[
 本节我们来学习html中a标签的使用:
 <body>
  <a href="index.html">首页</a>
 </body>
 ]]>
</content>
</lesson>
有序的子元素

在XML多层嵌套的子元素中,标签前后顺序应保持一致
<…> </…>

XML语义约束之DTD

XML文档结构正确,但可能不是有效的

  • 例如,员工档案XML中绝不允许出现 “植物品种” 标签。XML语义约束就是用于规定XML文档中允许出现哪些元素
  • XML语义约束由两种定义方式:DTDXML Schema

DTD(Document Type Definition, 文档类型定义) 是一种简单易用的语义约束方式
DTD文件的扩展名为.dtd [用于说明HTML中拥有哪些节点可以出现]

hr.dtd
<!ELEMENT hr (employee+)>
<!ELEMENT employee (name,age,salary,department)>
<!ATTLIST employee no CDATA "">
<!ELEMENT name (#PCDATA)>
...
定于hr节点下只允许出现1个employee子节点
<!ELEMENT hr (employee)>

employee节点下必须包含以下四个节点,且按顺序出现
<!ELEMENT employee (name,age,salary,department)
    
定义name标签体只能是文本,#PCDATA代表文本元素
<!ELEMENT name (#PCDATA)>
DTD定义节点数量
  • 如果某个子节点需要多个重复出现,则需要在子节点后增加相应的描述符
    [后面带个+号 最少出现一个]
hr节点下最少出现1个emploee子节点    [+]
<!ELEMENT hr (employee+)>

hr节点下可出现0..n个employee子节点  [*]
<!ELEMENT hr (employee*)>

hr节点下最多出现1个emploee子节点    [?]
<!ELEMENT hr (employee?)>
XML引用DTD文件
  • 在XML中使用**< !DOCTYPE >**标签来引用DTD文件
书写格式:
<!DOCTYPE 根节点 SYSTEM "dtd文件路径">
示例:
<!DOCTYPE hr SYSTEM "hr.dtd">
案例 [XML像是描述 dtd像是约束 ]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hr SYSTEM "hr.dtd">
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
<?xml version="1.0" encoding="UTF-8" ?>
  <!ELEMENT hr (employee+)>
  <!ELEMENT employee (name,age,salary,department)>
  <!--前后顺序必须匹配 一一对应-->
  <!ATTLIST employee no CDATA "">
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT age (#PCDATA)>
  <!ELEMENT salary (#PCDATA)>
  <!ELEMENT department (dname,address)>
  <!ELEMENT dname (#PCDATA)>
  <!ELEMENT address (#PCDATA)>
  <!--里面是纯文本节点 department有两个子节点-->

XML Schema(比dtd更高级)

  • XML Schema比DTD更为复杂,提供了多个功能
  • XML Schema提供了数据类型、格式限定、数据范围等特性
  • XML Schema是W3C标准
<?xml version="1.0" encoding="UTF-8" ?>
<hr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="hr.xsd">
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
  <element name="hr">
<!--complexType标签含义是复杂节点,包含子节点时必须使用这个标签-->
    <complexType>
      <sequence>
        <!-- 节点最少出现一次  -->
        <element name="employee" minOccurs="1">
          <complexType>
            <sequence>
              <element name="name" type="string"></element>
              <element name="age">
                <simpleType>
                  <restriction base="integer"> <!--给年龄做限定-->
                    <minInclusive value="18"></minInclusive>
                    <maxInclusive value="60"></maxInclusive>
                  </restriction>
                </simpleType>
              </element>
              <element name="department">
                <complexType>
                  <sequence>
                    <element name="dname" type="string"></element>
                    <element name="address" type="string"></element>
                  </sequence>
                </complexType>
              </element>
            </sequence>
          </complexType>
        </element>
      </sequence>
      <!--no在任何employee节点下必须存在-->
      <attribute name="no" type="string" use="required"></attribute>
    </complexType>
  </element>
</schema>

DOM文档对象模型

DOM(Document Object Model)定义了访问和操作XML文件的标准方法,DOM把XML文档作为树结构来看,能够通过DOM树来读写所有元素。

Dom4j[以java的形式解析xml]

Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台,具有性能优异、功能强大和极其易用的特点。

  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象
Dom4j对xml的解析读取和遍历

[原则是按照documents根节点和子节点依次类推的顺序对其进行分析、解析、提取]

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.List;

public class HrReader{
  public void readXml() throws DocumentException {
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    //SAXReader类是读取XML文件的核心类,用于将XML解析后
      SAXReader reader = new SAXReader();
      Document document = reader.read(file);
      //获取XML文档的根节点,即hr标签
      Element root = document.getRootElement();
      //elements方法用于获取指定的标签集合
      List<Element> employees = root.elements("employee");
      for (Element employee : employees){
       /* //element方法用于获取唯一的子节点对象
        Element name = employee.element("name");
        //getText()方法用于获取标签文本
        String empName = name.getText();
        System.out.println(empName);
        */
        System.out.println(employee.elementText("age"));
        System.out.println(employee.elementText("salary"));
        Element department = employee.element("department");
        System.out.println(department.elementText("dname"));
        System.out.println(department.element("address").getText());
        Attribute att = employee.attribute("no"); /*获取对应文本*/
        System.out.println(att.getText());
        System.out.println("====================");
        }
      }

  public static void main(String[] args) throws DocumentException {
    HrReader reader = new HrReader();
    reader.readXml();
  }
}
Dom4j更新(写入)XML
已追加写入的信息
<hr>
 <employee no="3311">
    <name>李铁柱</name>
    <age>28</age>
    <salary>3600</salary>
    <department>
      <dname>人事部</dname>
      <address>XX大厦-B105</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class HrWriter {
  public void weiteXml(){
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    SAXReader reader = new SAXReader();
    try {
      Document document = reader.read(file);
      Element root = document.getRootElement();
      //创建employee子节点 全新空子节点
      Element employee = root.addElement("employee");
      employee.addAttribute("no","3311");
      Element name = employee.addElement("name");
      name.setText("李铁柱");
      employee.addElement("age").setText("28");
      employee.addElement("salary").setText("3600");
      Element department = employee.addElement("department");
      department.addElement("dname").setText("人事部");
      department.addElement("address").setText("XX大厦-B105");
      //内存中组织的dom模型重新写入到对应文件中
      Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
      document.write(writer);
      writer.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    HrWriter hrWriter = new HrWriter();
    hrWriter.weiteXml();
  }
}

XPath路径表达式

  • XPath路径表达式是XML文档中查找数据的语言
  • 掌握XPath可以极大的提高在读取数据时的开发效率
  • 学习XPath本质就是掌握各种形式表达式的使用技巧
最常用的基本表达式
表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
路径表达式 结果
bookstore 选取bookstore元素的所有子节点
/bookstore 选取根元素bookstore
注释:假如路径起始于正斜杠(/), 则此路径始终代表到某元素的绝对路径
bookstore/book 选取属于bookstore的子元素的所有book元素
//book 选取所有book子元素,而不管它们在文档中的位置
bookstore//book 选取属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么位置
//@lang 选取名为lang的所有属性
XPath谓语表达式
路径表达式 结果
/bookstore/book[1] 选取属于bookstore子元素的第一个book元素
/bookstore/book[last()] 选取属于bookstore子元素的最后一个book元素
/bookstore/book[position()<3] 选取最前面的两个属于bookstore元素的子元素的book元素
//title[@lang] 选取所有拥有名为lang的属性的title元素
/bookstore/book[price>35.00] 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00
/bookstore/book[price>35.00]/title 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值必须大于35.00

XPath实验室 [优先使用 查询数据]

Jaxen介绍
  • Jaxen是一个Java编写的开源的XPath库。这是适合多种不同的对象模型,包括DOM,XOM,dom4j和JDOM
  • Dom4j底层一来Jaxen实现XPath查询
<?xml version="1.0" encoding="UTF-8"?>
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>3200</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>

  <employee no="3311">
    <name>李铁柱</name>
    <age>28</age>
    <salary>3600</salary>
    <department>
      <dname>人事部</dname>
      <address>XX大厦-B105</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.util.List;

public class XPathTestor {
  public void xpath(String xpathExp){
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    SAXReader reader = new SAXReader();
    try {
      Document document = reader.read(file);
      //执行xpath表达式 Node不仅查询标签还可以查询属性
      List<Node> nodes = document.selectNodes(xpathExp);
      for (Node node : nodes){
        //转换成常用的Element对象 父类node强转
        Element emp = (Element)node;
        System.out.println(emp.attribute("no"));
        System.out.println(emp.elementText("name"));
        System.out.println(emp.elementText("age"));
        System.out.println(emp.elementText("salary"));
        System.out.println("===========================");
      }
    } catch (DocumentException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) {
    XPathTestor testor = new XPathTestor();
    //单斜杠要按照根目录一级一级搜索
//    testor.xpath("/hr/employee");
    //双斜杠 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
//    testor.xpath("//employee");
//    testor.xpath("//employee[salary<4000]");
//    testor.xpath("//employee[name='李铁柱']");
//    testor.xpath("//employee[@no=3309]");
//    testor.xpath("//employee[1]");
//    testor.xpath("//employee[last()]");
//    testor.xpath("//employee[position()<6]"); 当前位置小于6
//    testor.xpath("//employee[1] | //employee[3]");
  }
}